package com.crpc.core;


import cn.hutool.core.util.ObjectUtil;
import com.crpc.common.annotation.CrpcAPi;
import com.crpc.common.crpcEnum.CrpcExceptionEnums;
import com.crpc.common.exception.CrpcException;
import com.crpc.core.channelHandler.ProviderChannelInitializer;
import com.crpc.core.config.Configuration;
import com.crpc.core.detector.HeartbeatDetector;
import com.crpc.core.discovery.RegistryConfig;
import com.crpc.core.loadBalance.LoadBalance;
import com.crpc.core.transport.message.CrpcRequest;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.Channel;
import io.netty.channel.ChannelFuture;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import lombok.extern.slf4j.Slf4j;

import java.io.File;
import java.net.InetSocketAddress;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.TreeMap;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.stream.Collectors;


/**
 * @author CLOUD
 * @version 1.0
 * @description:
 * @date 2023/9/7 13:04
 */
@Slf4j
public class CrpcBootStrap {

    //保存Request对象，在当前线程中，随时获取
    public static final ThreadLocal<CrpcRequest> CRPC_REQUEST_THREAD_LOCAL = new ThreadLocal<>();
    //netty连接池
    public static final Map<InetSocketAddress, Channel> CHANNEL_MAP = new ConcurrentHashMap<>(16);
    public static final TreeMap<Long, Channel> ANSWER_TIME = new TreeMap<>();
    //维护已经发布的服务列表key-> interface全限定名,value->ServiceConfig
    public static final Map<String, ServiceConfig<?>> SERVICE_LIST = new ConcurrentHashMap<>(16);
    //定义全局对外挂起的CompletableFuture
    public final static Map<Long, CompletableFuture<Object>> PENDING_REQUEST = new ConcurrentHashMap<>(128);
    //维护一个springboot的全局ServiceConfig集合缓存
    public final static List<ServiceConfig<?>> SPRINGBOOt_SERVICE_Configs = new CopyOnWriteArrayList<>();
    // 单例，每一个应用程序只有一个
    private static final CrpcBootStrap crpcBootStrap = new CrpcBootStrap();
    //全局配置中心
    private final Configuration configuration;

    //CrpcBootStrap是个单例
    private CrpcBootStrap() {
        configuration = new Configuration();
    }

    public static CrpcBootStrap getInstance() {
        return crpcBootStrap;
    }

    //外部获取 Configuration
    public Configuration getConfiguration() {
        return this.configuration;
    }

    //-------------------------------------------服务提供方--------------------------------------
    //定义当前应用的名字
    public CrpcBootStrap application(String appName) {
        configuration.setApplicationName(appName);
        return this;
    }

    //定义端口号
    public CrpcBootStrap port(String port) {
        configuration.setPort(port);
        return this;
    }

    //配置注册中心
    public CrpcBootStrap registry(RegistryConfig registryConfig) {
        configuration.setRegistryConfig(registryConfig);
        return this;
    }

    //负载均衡策略
    public CrpcBootStrap registry(LoadBalance loadBalance) {
        configuration.setLoadBalance(loadBalance);
        return this;
    }

    //服务发布
    public CrpcBootStrap publish(ServiceConfig<?> service) {
        //抽象注册中心
        configuration.getRegistryConfig().getRegistry().register(service);
        SERVICE_LIST.put(service.getInterfaceProvider().getName(), service);
        return this;
    }

    //批量服务发布
    public CrpcBootStrap publish(List<ServiceConfig<?>> services) {
        for (ServiceConfig<?> service : services) {
            log.info("通过包扫描发布服务====>{}", service.getInterfaceProvider().getName());
            configuration.getRegistryConfig().getRegistry().register(service);
            SERVICE_LIST.put(service.getInterfaceProvider().getName(), service);
        }
        return this;
    }

    //启动netty服务
    public void start() {
        NioEventLoopGroup boss = new NioEventLoopGroup(2);
        NioEventLoopGroup worker = new NioEventLoopGroup(10);
        try {
            ServerBootstrap bootstrap = new ServerBootstrap();
            bootstrap = bootstrap.group(boss, worker)
                    .channel(NioServerSocketChannel.class)
                    .childHandler(new ProviderChannelInitializer());
            ChannelFuture channelFuture = bootstrap.bind(Integer.parseInt(configuration.getPort())).sync();
            log.info("netty服务成功启动");
            channelFuture.channel().closeFuture().sync();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            try {
                boss.shutdownGracefully().sync();
                worker.shutdownGracefully().sync();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

    //-------------------------------------------服务调用方--------------------------------------
    public CrpcBootStrap reference(ReferenceConfig<?> reference) {
        //开启心跳检测
        HeartbeatDetector.detectorHeartbeat(reference.getInterfaceProvider().getName());
        reference.setRegistry(configuration.getRegistryConfig().getRegistry());
        return this;

    }

    /**
     * @description: 配置序列化的方式
     * @author wzy
     * @date 2023/9/29 15:33
     */
    public CrpcBootStrap serialize(String serializeType) {
        configuration.setSerializerType(serializeType);
        log.info("消费者配置了序列化的方式==>{}", serializeType);
        return this;
    }

    public CrpcBootStrap compress(String compressType) {
        configuration.setCompressorType(compressType);
        log.info("消费者配置了压缩的方式==>{}", compressType);
        return this;
    }

    public CrpcBootStrap scan(String packageName) {
        //通过packageName 获取下面所有类的权限定名称
        List<String> nameList = getAllClassName(packageName);
        List<Class<?>> classList = nameList.stream().map(className -> {
                    try {
                        return Class.forName(className);
                    } catch (ClassNotFoundException e) {
                        log.info("包扫描失败");
                        e.printStackTrace();
                        throw new CrpcException(CrpcExceptionEnums.SCAN_ERROR);
                    }
                }).filter(clazz -> clazz.getAnnotation(CrpcAPi.class) != null)
                .collect(Collectors.toList());
        if (classList.isEmpty()) {
            log.info("扫描结果为空，未找到@CrpcAPi注解的接口");
        }
        //通过反射获取接口，构建具体实现
        for (Class<?> clazz : classList) {
            Class<?>[] interfaces = clazz.getInterfaces();
            Object instance = null;
            try {
                instance = clazz.getConstructor().newInstance();
            } catch (Exception e) {
                e.printStackTrace();
                throw new RuntimeException(e);
            }
            List<ServiceConfig<?>> serviceConfigs = new ArrayList<>();
            for (Class<?> interfaceClazz : interfaces) {
                ServiceConfig<Object> serviceConfig = new ServiceConfig<>();
                serviceConfig.setInterfaceProvider((Class) interfaceClazz);
                serviceConfig.setRefImpl(instance);
                serviceConfigs.add(serviceConfig);
            }
            publish(serviceConfigs);
        }
        //发布
        return this;
    }

    private List<String> getAllClassName(String packageName) {
        //获得绝对路径 com.crpc.xxx   d://xx/xx/com/crpc/xx/xxx
        String basePath = packageName.replaceAll("\\.", "/");
        URL url = ClassLoader.getSystemClassLoader().getResource(basePath);
        if (ObjectUtil.isNull(url)) {
            throw new CrpcException(CrpcExceptionEnums.SCAN_ERROR);
        }
        String absolutePath = url.getPath();
        List<String> classNameList = new ArrayList<>();
        recursionFile(absolutePath, classNameList, basePath);
        return classNameList;
    }

    private void recursionFile(String absolutePath, List<String> classNameList, String basePath) {
        //获取文件
        File file = new File(absolutePath);
        //判断文件是否为文件夹
        if (file.isDirectory()) {
            File[] children = file.listFiles(pathname -> pathname.getPath().contains(".class") || pathname.isDirectory());
            if (ObjectUtil.isNull(children) || children.length == 0) {
                return;
            } else
                for (File child : children) {
                    if (child.isDirectory()) {
                        recursionFile(child.getAbsolutePath(), classNameList, basePath);
                    } else {
                        // 文件 ---> 类的权限定名称
                        //D:\EngineeringProject\Java\SpringBoot\springboot-demo\cloud-rpc\rpc-framework\rpc-core\target\classes\com\crpc\ReferenceConfig.class
                        String className = getClassNameByAbsolutePath(child.getAbsolutePath(), basePath);
                        classNameList.add(className);
                        System.out.println(className);
                    }
                }
        } else {
            String className = getClassNameByAbsolutePath(absolutePath, basePath);
            classNameList.add(className);
        }
    }

    private String getClassNameByAbsolutePath(String absolutePath, String basePath) {
        //D:\EngineeringProject\Java\SpringBoot\springboot-demo\cloud-rpc\rpc-framework\rpc-core\target\classes\com\crpc\ReferenceConfig.class
        //com\crpc\ReferenceConfig.class ---> com.crpc.core.ReferenceConfig
        String substring = absolutePath.substring(absolutePath.indexOf(basePath.replaceAll("/", "\\\\")))
                .replaceAll("\\\\", ".");
        substring = substring.substring(0, substring.indexOf(".class"));
        return substring;
    }
}
